<?php
/**
 * Created by PhpStorm
 * User: whh
 * Date: 2022/12/6
 * Time: 10:08
 */
declare(strict_types=1);

namespace app\common\command;

use app\common\server\TcpServer;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
use think\facade\Env;

class Swoole extends Command
{
    private $host = "0.0.0.0";
    private $port = 9508;
    private $config = [
        'reactor_num' => 8,// 线程数。【默认值：CPU 核数】 用php -r "echo swoole_cpu_num();"查询cpu核数，考虑到操作系统调度存在一定程度的性能损失，可以设置为 CPU 核数 * 2，以便最大化利用 CPU 的每一个核。
        'worker_num' => 8,// 进程数 但开的进程越多，占用的内存就会大大增加，而且进程间切换的开销就会越来越大。所以这里适当即可。不要配置过大。 最大不得超过 swoole_cpu_num() * 1000，建议开启的worker进程数为cpu核数的1-4倍。
        'dispatch_mode' => 1,// 数据包分发策略 SWOOLE_PROCESS【多进程模式（默认）】设置为1或3
        'max_request' => 100,//设置 worker 进程的最大任务数。
        'daemonize' => false,//true为守护进程运行
        'task_worker_num' => 100,//最大值不得超过 swoole_cpu_num() * 1000
        //'task_max_request' => 0,
        'task_enable_coroutine' => true,//开启Task允许使用协程支持
        'max_wait_time' => 60,
        'reload_async' => true,
    ];

    protected function configure()
    {
        $this->setName("swoole")
            ->addArgument('action', Argument::OPTIONAL, "方法名称")
            ->addOption('cmd', null, Option::VALUE_REQUIRED, '执行命令')
            ->setDescription('swoole服务');
    }

    protected function execute(Input $input, Output $output)
    {
        $action = $input->getArgument('action');

        if ($input->hasOption('cmd')) {
            $cmd = $input->getOption('cmd');
        } else {
            $cmd = '';
        }

        if (method_exists($this, $action)) {
            if ($action == 'tcp_cmd') {
                $this->tcp_cmd($cmd);
            } else {
                $this->$action();
            }
        } else {
            /** @var TYPE_NAME $output */
            die($output->writeln("not method: " . __CLASS__ . "->" . $action));
        }
        $output->writeln(__CLASS__ . " console end;");
    }

    /**
     * tcp服务启动
     * @Author: whh
     */
    public function tcp_start()
    {
        $this->config['log_file'] = Env::get('runtime_path') . '/log/tcp.log';
        $tcp = new TcpServer($this->config, $this->host, $this->port);
        $tcp->run();
    }


    /**
     * tcp服务关闭
     * @Author: whh
     */
    public function tcp_shutdown()
    {
        echo "tcp shutdown..." . PHP_EOL;
        $this->tcp_cmd('_shutdown');
    }


    /**
     * tcp重启
     * 要注意新修改的代码必须要在 OnWorkerStart 事件中重新载入才会生效，
     * 比如某个类在 OnWorkerStart 之前就通过 composer 的 autoload 载入了就是不可以的。
     * 所以改TcpServer代码，重启这个无效
     * 可以在 WorkerStart打印get_included_files()函数来列出哪些文件是在 WorkerStart之前的
     * @Author: whh
     */
    public function tcp_reload()
    {
        echo "tcp reload..." . PHP_EOL;
        $this->tcp_cmd('_reload');
    }


    /**
     * 自定义命令
     * @param $cmd
     * @Author: whh
     */
    public function tcp_cmd($cmd)
    {
        $fp = stream_socket_client('tcp://127.0.0.1:9508', $errno, $errstr, 1);
        if (!$fp) {
            echo "{$errstr} ({$errno})\n";
        } else {
            $content = json_encode([
                'cmd' => $cmd,
            ]);
            fwrite($fp, $content);
            $data = fread($fp, 1024);
            echo $data;
            fclose($fp);
        }
    }
}